package main

import "fmt"

/*
指针是一个存储变量内存地址的变量。它们允许程序直接访问和操作内存中的数据，而不是对数据的副本进行操作。以下是指针的一些关键概念：
内存地址： 每个变量在计算机内存中都有一个唯一的地址，指针存储了这个地址。
指针变量： 用于存储其他变量地址的变量称为指针变量。
取地址操作符（&）： 可以使用取地址操作符&来获取变量的地址。
解引用操作符（*）： 可以使用解引用操作符*来访问指针所指向的变量的值。
Go语言中的值类型（int、float、bool、string、array、struct）都有对应的指针类型，如：*int、*int64、*string等。
Go 语言中的指针与 C/C++ 中的指针类似，但是 Go 语言中的指针不能直接进行指针运算(只能通过unsafe包)，也不能进行指针类型转换
Go语言中可以不使用解引操作符直方法指针指向的变量的值，直接用 . 访问目标成员.
*/

type people struct {
	name string
	age  int
}

func main() {
	/*
		一、指针的定义
		语法： var 指针变量名称 *指针类型
	*/
	var in *int          //定义一个指向int类型的指针
	var str *string      //定义一个指向string类型的指针
	fmt.Println(in, str) //<nil> <nil> 没存储任何地址，表示空指针
	/*
		二、指针初始化的几种方式
	*/
	// 第一种方式&    为指针变量获取指向变量的内存地址
	var p1 *int      //定义int类型的指针变量
	a := 19          //初始化int类型的变量
	p1 = &a          //将p1指向a的内存地址，进行指针变量的初始化
	*p1 = 8          //通过*解引符修改指向的int变量的值
	fmt.Println(*p1) //单独基础类型的时候还是需要*来或取值
	fmt.Println(p1)  //指向指向的地址 0xc00000a110
	fmt.Println(&a)  //变量a的地址	0xc00000a110
	fmt.Println(&p1) // 指向本身的地址	0xc000064030

	// 第二种方式 new()函数 -----map channl slice初始化推荐make()，指针和struct的初始化则推荐new()
	var p2 *int
	p2 = new(int) //初始化一块内存空间存放地址，再初始化一块内存存放值
	*p2 = 9       //把9放到存放值的空间，并把这个空间的地址存放到地址存放空间
	fmt.Println(*p2)
	fmt.Println(p2)
	fmt.Printf("%p\n", &p2)

	/*
		三、集合类型数据结构的指针初始化
	*/
	//1、new()定义初始化指针
	var p3 *people = new(people)
	*p3 = people{name: "jiang", age: 18} //给指向的值内存地址赋值
	fmt.Println(p3)                      //对于结构体集合类型的时候可以不用*来获取值，不过会带&号，如&{jiang 18}
	fmt.Println(*p3)                     //这个比较干净{jiang 18}
	p3.name = "小李"                       //并且可以象平常一样操作结构体的字段，不用特殊符号

	//第二种 初始化
	p4 := &people{name: "jiang", age: 18}
	fmt.Println(*p4)

	//第三种 初始化
	var per3 people
	p5 := &per3
	fmt.Println(p5)
	/*
		总结：new()方式初始化相当于开辟了值存储空间和指针(值地址)存储空间，并把值存储空的地址给了指针，使用的时候只要给值存储空间赋值就行  *号
			单独先定义，只是开辟了地址存储空间，我们还需把对应值的地址给存入这个空间 &取地址符合
			并结构体指针的时候，操作结构体字段的时候和平常一样，无需带特殊字符
	*/
}
